const ts = require("./typescript");

const directorySeparator = "/";
const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/;
const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g;
const WatchDirectoryFlags = {
  None: 0,
  Recursive: 1 << 0,
};

function hasProperty(map, key) {
  return hasOwnProperty.call(map, key);
}

function toLowerCase(x) {
  return x.toLowerCase();
}

function isImplicitGlob(lastPathComponent) {
  return !/[.*?]/.test(lastPathComponent);
}

function toFileNameLowerCase(x) {
  return fileNameLowerCaseRegExp.test(x)
    ? x.replace(fileNameLowerCaseRegExp, toLowerCase)
    : x;
}

function getWildcardDirectoryFromSpec(spec, useCaseSensitiveFileNames) {
  const match = wildcardDirectoryPattern.exec(spec);
  if (match) {
    // We check this with a few `indexOf` calls because 3 `indexOf`/`lastIndexOf` calls is
    // less algorithmically complex (roughly O(3n) worst-case) than the regex we used to use,
    // \/[^/]*?[*?][^/]*\/ which was polynominal in v8, since arbitrary sequences of wildcard
    // characters could match any of the central patterns, resulting in bad backtracking.
    const questionWildcardIndex = spec.indexOf("?");
    const starWildcardIndex = spec.indexOf("*");
    const lastDirectorySeperatorIndex = spec.lastIndexOf(directorySeparator);
    return {
      key: useCaseSensitiveFileNames ? match[0] : toFileNameLowerCase(match[0]),
      flags:
        (questionWildcardIndex !== -1 &&
          questionWildcardIndex < lastDirectorySeperatorIndex) ||
        (starWildcardIndex !== -1 &&
          starWildcardIndex < lastDirectorySeperatorIndex)
          ? WatchDirectoryFlags.Recursive
          : WatchDirectoryFlags.None,
    };
  }
  if (isImplicitGlob(spec)) {
    return {
      key: useCaseSensitiveFileNames ? spec : toFileNameLowerCase(spec),
      flags: WatchDirectoryFlags.Recursive,
    };
  }
  return undefined;
}

/**
 * Gets directories in a set of include patterns that should be watched for changes.
 */
function getWildcardDirectories(
  {validatedIncludeSpecs: include, validatedExcludeSpecs: exclude},
  path,
  useCaseSensitiveFileNames
) {
  // We watch a directory recursively if it contains a wildcard anywhere in a directory segment
  // of the pattern:
  //
  //  /a/b/**/d   - Watch /a/b recursively to catch changes to any d in any subfolder recursively
  //  /a/b/*/d    - Watch /a/b recursively to catch any d in any immediate subfolder, even if a new subfolder is added
  //  /a/b        - Watch /a/b recursively to catch changes to anything in any recursive subfoler
  //
  // We watch a directory without recursion if it contains a wildcard in the file segment of
  // the pattern:
  //
  //  /a/b/*      - Watch /a/b directly to catch any new file
  //  /a/b/a?z    - Watch /a/b directly to catch any new file matching a?z
  const rawExcludeRegex = ts.getRegularExpressionForWildcard(
    exclude,
    path,
    "exclude"
  );
  const excludeRegex =
    rawExcludeRegex &&
    new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i");

  return excludeRegex;
  const wildcardDirectories = {};
  if (include !== undefined) {
    const recursiveKeys = [];
    for (const file of include) {
      const spec = ts.normalizePath(ts.combinePaths(path, file));
      if (excludeRegex && excludeRegex.test(spec)) {
        continue;
      }

      const match = getWildcardDirectoryFromSpec(
        spec,
        useCaseSensitiveFileNames
      );
      if (match) {
        const {key, flags} = match;
        const existingFlags = wildcardDirectories[key];
        if (existingFlags === undefined || existingFlags < flags) {
          wildcardDirectories[key] = flags;
          if (flags === WatchDirectoryFlags.Recursive) {
            recursiveKeys.push(key);
          }
        }
      }
    }

    // Remove any subpaths under an existing recursively watched directory.
    for (const key in wildcardDirectories) {
      if (hasProperty(wildcardDirectories, key)) {
        for (const recursiveKey of recursiveKeys) {
          if (
            key !== recursiveKey &&
            ts.containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)
          ) {
            delete wildcardDirectories[key];
          }
        }
      }
    }
  }

  return wildcardDirectories;
}

module.exports = {
  getWildcardDirectories,
};
